package cc.cix.hq.common.data.permission.inteceptor;

import cc.cix.hq.common.core.entity.constant.StringConstant;
import cc.cix.hq.common.core.entity.org.User;
import cc.cix.hq.common.core.utils.HqUtil;
import cc.cix.hq.common.data.permission.annotation.DataPermission;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.io.StringReader;
import java.util.List;
import java.util.stream.Collectors;

/**
 * SQL拦截器，用于动态注入数据权限SQL
 *
 * @author cix
 */
@Slf4j
public class DataPermissionInterceptor implements InnerInterceptor {

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
                            RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        try {
            String className = ms.getId().substring(0, ms.getId().lastIndexOf(StringConstant.DOT));
            Class<?> aClass = Class.forName(className);
            if (!aClass.isAnnotationPresent(DataPermission.class)) {
                return;
            }

            PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
            DataPermission dataPermission = aClass.getAnnotation(DataPermission.class);

            // 判断是否是可过滤方法
            String methodName = ms.getId().replace(className + ".", "");
            List<String> methods = CollUtil.newArrayList(dataPermission.methods());
            if (methods.isEmpty() || methods.contains(methodName)) {
                mpBoundSql.sql(dataPermissionSql(boundSql.getSql(), dataPermission));
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private String dataPermissionSql(String originSql, DataPermission dataPermission) {
        try {
            User currentUser = HqUtil.getCurrentUser();
            if (currentUser == null) {
                return originSql;
            }
            CCJSqlParserManager parserManager = new CCJSqlParserManager();
            Select select = (Select) parserManager.parse(new StringReader(originSql));
            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
            Table fromItem = (Table) plainSelect.getFromItem();

            // 获取用户部门 ID 列表
            String deptIds = HqUtil.getCurrentUser().getDeptIds()
                    .stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(StringConstant.COMMA));
            String dataPermissionSql;
            String selectTableName = fromItem.getAlias() == null ? fromItem.getName() : fromItem.getAlias().getName();

            // 如果有部门则进行注入，无部门默认没有权限
            if (StrUtil.isNotBlank(deptIds)) {
                dataPermissionSql = String.format("%s.%s in (%s)", selectTableName, dataPermission.field(), deptIds);
            } else {
                dataPermissionSql = String.format("%s.%s in (%s)", selectTableName, dataPermission.field(), "0");
            }

            if (plainSelect.getWhere() == null) {
                plainSelect.setWhere(CCJSqlParserUtil.parseCondExpression(dataPermissionSql));
            } else {
                plainSelect.setWhere(new AndExpression(plainSelect.getWhere(), CCJSqlParserUtil.parseCondExpression(dataPermissionSql)));
            }
            return select.toString();
        } catch (Exception e) {
            log.warn("get data permission sql fail: {}", e.getMessage());
            return originSql;
        }
    }

}
